package sg.edu.nus.iss.vmcs.store;

/*
 * Copyright 2003 ISS.
 * The contents contained in this document may not be reproduced in any
 * form or by any means, without the written permission of ISS, other
 * than for the purpose for which it has been supplied.
 *
 */

import java.io.*;

import sg.edu.nus.iss.vmcs.iterator.CashCoinDenoIterator;
import sg.edu.nus.iss.vmcs.system.ItemLoader;
import sg.edu.nus.iss.vmcs.system.PropertyImp;
import sg.edu.nus.iss.vmcs.util.PropertyException;

/**
 * This control object manages changes in Cash Store attributes and the Drinks
 * Store attributes.
 * 
 * @version 3.0 5/07/2003
 * @author Olivo Miotto, Pang Ping Li
 */

public class StoreController {

	private CashStore cStore;
	private DrinksStore dStore;

	// modified by chen wei
	//private PropertyLoader cashLoader;
	//private PropertyLoader drinksLoader;
	
	private ItemLoader cashLoader;
	private ItemLoader drinksLoader;
	
	private PropertyImp cashPropImp;
	private PropertyImp drinksPropImp;
	// end of modified by chen wei

	/**
	 * This consturctor creates an instance of the Store Controller. It will
	 * then create the Cash Store, Drinks Store and Door.
	 * 
	 * @param cashLoader
	 *            {@link sg.edu.nus.iss.vmcs.store.PropertyLoader}
	 * @param drinksLoader
	 *            {@link sg.edu.nus.iss.vmcs.store.PropertyLoader}
	 */
	// modified by chen wei
	/*public StoreController(PropertyLoader cashLoader,
			PropertyLoader drinksLoader) {
		this.cashLoader = cashLoader;
		this.drinksLoader = drinksLoader;
	}*/
	
	public StoreController(ItemLoader cashLoader, ItemLoader drinksLoader, PropertyImp cashPropImp, PropertyImp drinksPropImp) {
		this.cashLoader = cashLoader;
		this.drinksLoader = drinksLoader;
		
		this.cashPropImp = cashPropImp;
		this.drinksPropImp = drinksPropImp;
	}
	// end of modified by chen wei

	/**
	 * This operation initiates the initialisation of the Drink Store and the
	 * Cash Store from data read-in form an input file.
	 */
	public void initialize() throws IOException {
		cStore = new CashStore();
		dStore = new DrinksStore();
		initializeStores();
	}

	/**
	 * This operation initializes the Cash Store.
	 */
	private void initializeStores() throws IOException {
		initializeCashStore();
		initializeDrinkStore();
	}

	/**
	 * This operation initializes the Drink Store.
	 */
	private void initializeDrinkStore() throws IOException {

		// get the drink file from the environment property file;
		int numOfItems = drinksLoader.getNumOfItems();
		dStore.setStoreSize(numOfItems);

		for (int i = 0; i < numOfItems; i++) {
			DrinksStoreItem item = (DrinksStoreItem) drinksLoader.getItem(i);
			StoreObject brand = item.getContent();
			StoreObject existingBrand = dStore.findObject(brand.getName());
			if (existingBrand != null) {
				item.setContent(existingBrand);
			}
			dStore.addItem(i, item);
		}
	}

	/**
	 * This operation initializes the Cash Store.
	 */
	private void initializeCashStore() throws IOException {

		// get the cash file from the environment property file;
		int numOfItems = cashLoader.getNumOfItems();
		cStore.setStoreSize(numOfItems);

		for (int i = 0; i < numOfItems; i++) {
			CashStoreItem item = (CashStoreItem) cashLoader.getItem(i);
			cStore.addItem(i, item);
		}
	}

	/**
	 * This operation will instruct the Cash Store to store the Coin sent as
	 * input, and update the display on the Machinery Simulator Panel.
	 */
	public void storeCoin(Coin c) {
		int idx = cStore.findCashStoreIndex(c);
		CashStoreItem item;
		item = (CashStoreItem) this.getStoreItem(Store.CASH, idx);
		item.increment();
	}

	/**
	 * This operation gets the size of the given Store type.
	 * 
	 * @param type
	 *            Store type constant defined in
	 *            {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return Number of items in the store.
	 */
	public int getStoreSize(int type) {
		if (type == Store.CASH)
			return cStore.getStoreSize();
		else
			return dStore.getStoreSize();
	}

	/**
	 * This operation gets the content of the given Store type as
	 * {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} array
	 * 
	 * @param type
	 *            Store type constant defined in
	 *            {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} array
	 */
	public StoreItem[] getStoreItems(int type) {
		if (type == Store.CASH)
			return cStore.getItems();
		else
			return dStore.getItems();
	}

	/**
	 * This operation will either: - instruct the Cash Store to update the
	 * quantity of a coin denomination to the new value supplied and update the
	 * total cash held in the Cash Store; or - instruct the Drinks Store to
	 * update the drinks stock for a drinks brand requested to a new value
	 * supplied.
	 * 
	 * @param type
	 *            Store type constant defined in
	 *            {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @param idx
	 *            Zero-based index of the Store Item
	 * @param qty
	 *            Quantity
	 */
	public void changeStoreQty(int type, int idx, int qty) {
		System.out.println("StoreController.changeStoreQty: type:" + type
				+ "   qty:" + qty);
		if (type == Store.CASH)
			cStore.setQuantity(idx, qty);
		else
			dStore.setQuantity(idx, qty);
	}

	/**
	 * This operation will return the
	 * {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} of the given Store
	 * Type at the given index.
	 * 
	 * @param type
	 *            Store type constant defined in
	 *            {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @param idx
	 *            Zero-based index of the
	 *            {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem}
	 * @return {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} at the
	 *         given index of the given Store Type.
	 */
	public StoreItem getStoreItem(int type, int idx) {
		if (type == Store.CASH)
			return cStore.getStoreItem(idx);
		else
			return dStore.getStoreItem(idx);
	}

	/**
	 * This operation will set the price of
	 * {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksDrand} at the given
	 * index of the {@link sg.edu.nus.iss.vmcs.store.DrinksStore DrinksStore} It
	 * will use the
	 * {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem#getContent()
	 * getContent()} method of the
	 * {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem DrinksStoreItem} to get
	 * the {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksBrand} and
	 * {@link sg.edu.nus.iss.vmcs.store.DrinksBrand#setPrice(int) setPrice(int)}
	 * method of the {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksBrand}
	 * to change the price.
	 * 
	 * @param idx
	 *            Zero-based index of the
	 *            {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem
	 *            DrinksStoreItem}
	 * @param pr
	 *            Price
	 */
	public void setPrice(int idx, int pr) {
		DrinksStoreItem item;

		item = (DrinksStoreItem) dStore.getStoreItem(idx);
		DrinksBrand bd;

		bd = (DrinksBrand) item.getContent();

		bd.setPrice(pr);
	}

	/**
	 * This operation returns the total cash held in the
	 * {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store}.
	 * 
	 * @return Total value of cash held in the
	 *         {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store}
	 */
	public int getTotalCash() {
		//int i;
		//int size;

		//size = cStore.getStoreSize();
		CashStoreItem item;
		int qty;
		int val;
		int tc = 0;
		Coin c;
		CashCoinDenoIterator lCashCoinDenoIterator = new CashCoinDenoIterator(
				cStore.getItems(), false);

		//item = (CashStoreItem) lCashCoinDenoIterator.first();
		while (!lCashCoinDenoIterator.isDone()) {
			item = (CashStoreItem) lCashCoinDenoIterator.next();
			qty = item.getQuantity();
			c = (Coin) item.getContent();
			val = c.getValue();
			tc = tc + qty * val;

			

		}
		return tc;
	}

	/**
	 * This operation is to facilitate the transfer of all cash in
	 * {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store} to the maintainer.
	 * 
	 * @return Total number of coins transferred
	 */
	public int transferAll() {
		// int i;
		int cc = 0; // coin quauntity;
		// int size = cStore.getStoreSize();

		CashStoreItem item;
		CashCoinDenoIterator lCashCoinDenoIterator = new CashCoinDenoIterator(
				cStore.getItems(), false);
		//item = (CashStoreItem) lCashCoinDenoIterator.first();

		while (!lCashCoinDenoIterator.isDone()) {
			item = (CashStoreItem) lCashCoinDenoIterator.next();
			cc = cc + item.getQuantity();
			item.setQuantity(0);
			
		}

		return cc;
	}

	/**
	 * This operation will close down the store management function of the
	 * vending machine. This involves saving the attributes of the stores to the
	 * property files.
	 */
	// modified by chen wei
	/*public void closeDown() throws IOException {
		// save back cash property;
		saveCashProperties();
		saveDrinksProperties();
	}*/
	
	public void closeDown() throws PropertyException {
		// save back cash property;
		saveCashProperties();
		saveDrinksProperties();
	}
	// end of modified by chen wei

	/**
	 * This operation persists the {@link sg.edu.nus.iss.vmcs.store.CashStore
	 * Cash Store} information when the simulation is ended.
	 */
	// modified by chen wei
	/*private void saveCashProperties() throws IOException {
		int size = cStore.getStoreSize();
		cashLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			cashLoader.setItem(i, cStore.getStoreItem(i));
		}
		cashLoader.saveProperty();
	}*/
	
	private void saveCashProperties() throws PropertyException {
		int size = cStore.getStoreSize();
		cashLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			cashLoader.setItem(i, cStore.getStoreItem(i));
		}
		cashPropImp.saveProperty();
	}
	// end of modified by chen wei

	/**
	 * This operation persists the {@link sg.edu.nus.iss.vmcs.store.DrinksStore
	 * DrinksStore} information when the simulation is ended.
	 */
	// modified by chen wei
	/*private void saveDrinksProperties() throws IOException {
		int size = dStore.getStoreSize();
		drinksLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			drinksLoader.setItem(i, dStore.getStoreItem(i));
		}
		drinksLoader.saveProperty();
	}*/
	
	private void saveDrinksProperties() throws PropertyException {
		int size = dStore.getStoreSize();
		drinksLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			drinksLoader.setItem(i, dStore.getStoreItem(i));
		}
		drinksPropImp.saveProperty();
	}
	// end of modified by chen wei

	/**
	 * This operation instructs the Drinks Store to dispense one drink, and then
	 * updates the Machinery Simulator Panel. It returns TRUE or FALSE to
	 * indicate whether dispensing was successful.
	 * 
	 * @param idx
	 *            Zero-based index of the Drink slot
	 */
	public void dispenseDrink(int idx) {
		DrinksStoreItem item;
		item = (DrinksStoreItem) getStoreItem(Store.DRINK, idx);
		item.decrement();
	}

	/**
	 * This operation return the {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * based on the given Store type.
	 * 
	 * @param type
	 *            Store type constant defined in
	 *            {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 */
	public Store getStore(int type) {
		if (type == Store.CASH)
			return (Store) cStore;
		else
			return (Store) dStore;
	}

	/**
	 * This operation instructs the Cash Store to issue a number of coins of a
	 * specific denomination, and then updates the Machinery Simulator Panel. It
	 * returns TRUE or FALSE to indicate whether the change issue was
	 * successful.
	 * 
	 * @param idx
	 *            Zero-based index of denomination held in Cash Store
	 * @param numOfCoins
	 *            Number of coins to deduct
	 */
	public void giveChange(int idx, int numOfCoins) {
		CashStoreItem item;
		item = (CashStoreItem) getStoreItem(Store.CASH, idx);
		for (int i = 0; i < numOfCoins; i++)
			item.decrement();
	}
}
